using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Perovich.GameObjects.Interfaces;


namespace Perovich.GameObjects.ThreeDimensional
{

    /// <summary>
    /// The <see cref="DrawableObject3D"/> is a <see cref="GameObject"/>, that is an abstract base class for any object that exists in three dimensions.
    /// </summary>
    public abstract class DrawableObject3D : GameObject, ICanBeAttachedTo, IAm3D
    {

        private Vector3 _position;
        private Vector3 _speed;
        private Vector3 _accel;
        private Vector3 LastPos;
        private float _rotationX;
        private float _rotationY;
        
        /// <summary>
        /// Gets or sets the <see cref="DrawableObject3D"/>'s rotation about the X-axis.
        /// </summary>
        public float RotationX
        {
            get { return _rotationX; }
            set { _rotationX = value; }
        }
        
        /// <summary>
        /// Gets or sets the <see cref="DrawableObject3D"/>'s rotation about the Y-axis.
        /// </summary>
        public float RotationY
        {
            get { return _rotationY; }
            set { _rotationY = value; }
        }



        

        
        /// <summary>
        /// Triggered whenever the <see cref="DrawableObject3D.Position"/> property is changed.
        /// </summary>
        public event EventHandler PositionChanged;

        /// <summary>
        /// Gets or sets the position of this <see cref="DrawableObject3D"/>.
        /// </summary>
        public Vector3 Position
        {
            get { return _position; }
            set {
                LastPos = _position;
                _position = value;
                if (_position != LastPos)
                    SignalPositionChanged();
            }
        }

        void SignalPositionChanged()
        {
            if (PositionChanged != null)
            {
                PositionChanged(this, new EventArgs());
            }
        }

        /// <summary>
        /// Gets or sets the velocity of this <see cref="DrawableObject3D"/>.
        /// </summary>
        public Vector3 Velocity
        {
            get { return _speed; }
            set { _speed = value; }
        }

        /// <summary>
        /// Gets or sets the acceleration of this <see cref="DrawableObject3D"/>.
        /// </summary>
        public Vector3 Acceleration
        {
            get { return _accel; }
            set { _accel = value; }
        }





        /// <summary>
        /// Constructs a new instance of the <see cref="DrawableObject3D"/> class.
        /// </summary>
        /// <param name="game"><see cref="Perovich.GameObjects.Interfaces.ICanHoldComponents"/> instance that this <see cref="DrawableObject3D"/> will be constructed for.</param>
        protected DrawableObject3D(Interfaces.ICanHoldComponents game)
            : base(game)
        {
            _position = Vector3.Zero;
            _speed = Vector3.Zero;
            _accel = Vector3.Zero;
            Scale = 1;
            Active = true;
        }




        /// <summary>
        /// Allows the <see cref="DrawableObject3D"/> to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            if (gameTime == null)
                throw new ArgumentNullException("gameTime");
            if (_speed.Length() != 0.0f)
                LastPos = _position;
            _position += _speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            _speed += _accel * (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (_position != LastPos)
                SignalPositionChanged();
            base.Update(gameTime);
        }



        /// <summary>
        /// Returns the <see cref="DrawableObject3D"/> to the position it was in before the Last Update.
        /// </summary>
        /// <example>This method can be called inside a collision handler do return the object to its position just before the collision.</example>
        public void Backup()
        {
            Position = LastPos;
        }

    }
}
